package com.xiaoxu.media.service.jobHandler;

import com.xiaoxu.base.utils.MinIOFilePathUtil;
import com.xiaoxu.base.utils.Mp4VideoUtil;
import com.xiaoxu.media.model.po.MediaProcess;
import com.xiaoxu.media.service.MediaFileService;
import com.xiaoxu.media.service.MediaProcessService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author xiaoxu
 * @version 1.0
 * @description TODO
 * @date 2023/1/25 16:56
 */
@Slf4j
@Service
public class VideoTask {

    private final MediaFileService mediaFileService;
    private final MediaProcessService mediaProcessService;


    @Value("${video.process.ffmpeg.path}")
    private String ffmpegPath;

    public VideoTask(@Lazy MediaFileService mediaFileService,@Lazy MediaProcessService mediaProcessService) {
        this.mediaFileService = mediaFileService;
        this.mediaProcessService = mediaProcessService;
    }

    @XxlJob("videoJobHandler")
    public void videoJobHandler() throws Exception {
        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();
        List<MediaProcess> mediaProcessList;
        int size;

        try {
            //取出2条记录，一次处理视频数量不要超过cpu核心数
            mediaProcessList = mediaProcessService.getMediaProcessList(shardIndex, shardTotal, 2);
            size = mediaProcessList.size();
            log.debug("取出待处理视频任务{}条", size);
            if (size <= 0) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        //启动size个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(size);
        //计数器
        CountDownLatch countDownLatch = new CountDownLatch(size);
        //将处理任务加入线程池
        mediaProcessList.forEach(mediaProcess -> threadPool.execute(() -> {
            //下边是处理逻辑
            //桶
            String bucket = mediaProcess.getBucket();
            //存储路径
            String filePath = mediaProcess.getFilePath();
            //原始视频的md5值
            String fileId = mediaProcess.getFileId();
            //原始文件名称
            String filename = mediaProcess.getFilename();
            //状态  2为已处理  1为未处理  3为报错
            String status = mediaProcess.getStatus();
            //判断该任务是否已经处理
            if ("2".equals(status)) {
                log.debug("视频已经处理成功，不再处理,文件:{},路径:{}", filename, filePath);
                return;
            }
            //将要处理的文件下载到服务器上
            File originalFile;
            //处理结束的视频文件
            File mp4File;

            try {
                originalFile = File.createTempFile("original", null);
                mp4File = File.createTempFile("mp4", ".mp4");
            } catch (IOException e) {
                log.error("处理视频前创建临时文件失败");
                countDownLatch.countDown();
                return;
            }
            try {
                //下载文件
                mediaFileService.downloadFileFromMinIO(originalFile, mediaProcess.getBucket(), mediaProcess.getFilePath());
            } catch (Exception e) {
                log.error("处理视频前下载原始文件:{},出错:{}", mediaProcess.getFilePath(), e.getMessage());
                countDownLatch.countDown();
                return;
            }
            //视频处理结果
            String result;
            try {
                //开始处理视频
                Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpegPath, originalFile.getAbsolutePath(), mp4File.getName(), mp4File.getAbsolutePath());
                //开始视频转换，成功将返回success
                result = videoUtil.generateMp4();
            } catch (Exception e) {
                log.error("处理视频文件:{},出错:{}", mediaProcess.getFilePath(), e.getMessage());
                countDownLatch.countDown();
                return;
            }
            if (!result.equals("success")) {
                //记录错误信息
                log.error("处理视频失败,视频地址:{},错误信息:{}", bucket + filePath, result);
                mediaProcessService.updateProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, result);
                countDownLatch.countDown();
                return;
            }

            //将mp4上传至minio
            //文件路径
            String objectName = null;
            try {
                objectName = MinIOFilePathUtil.getFilePathByMd5(fileId, ".mp4");
                FileInputStream fileInputStream = new FileInputStream(mp4File);
                byte[] bytes = new byte[(int)mp4File.length()];
                fileInputStream.read(bytes);
                fileInputStream.close();
                mediaFileService.uploadFileToMinio(bytes, bucket, objectName);
            } catch (Exception e) {
                log.error("上传视频失败,视频地址:{},错误信息:{}", bucket + objectName, e.getMessage());
                countDownLatch.countDown();
                return;
            }

            try {
                //访问url
                String url = "/" + bucket + "/" + objectName;
                //将url存储至数据，并更新状态为成功，并将待处理视频记录删除存入历史
                mediaProcessService.updateProcessFinishStatus(mediaProcess.getId(), "2", fileId, url, null);
            } catch (Exception e) {
                log.error("视频信息入库失败,视频地址:{},错误信息:{}", bucket + objectName, e.getMessage());
            }
            countDownLatch.countDown();
        }));
        //等待,给一个充裕的超时时间,防止无限等待，到达超时时间还没有处理完成则结束任务
        countDownLatch.await(30, TimeUnit.MINUTES);
    }

}

